#ifndef __demo_udp_vlink_kenny_kfjiweur89u83r33e3e__
#define __demo_udp_vlink_kenny_kfjiweur89u83r33e3e__

#include <typeinfo>

#include <hude/base/log.hpp>
#include <hude/base/hex.hpp>
#include <hude/aio/network.hpp>

#include "test/test.hpp"

namespace test
{
    using namespace hude;
  
    /*********************************************************************
     * @brief 定义网络工具集
     */
    using net_t = hude::net::net_t<>;

    /*********************************************************************
     * @brief 该示例建立一对UDP虚拟链路通信，由客户端发起一桢数据，
     * 之后服务端与客户端互相回显该数据，并且每次回显少发一个字节，直到没有数据可回显。
     */
    struct demo_udp_vlink_t : public hude::test::case_t
    {
        /*********************************************************************
         * @brief 测试结果
         */
        int result;

        /*********************************************************************
         * @brief 声明IO上下文
         */
        net_t::ctx_t aio;

        /*********************************************************************
         * @brief case_udp_listen_t
         */
        demo_udp_vlink_t() : result( 0 ) {}

        /*********************************************************************
         * @brief run
         */
        bool run() override
        {
            enum { emPORT = 2222U };

            //声明IO上下文
            aio = hude::aio::context_t<>::ctx();
    
            auto cb_entry = [=]
            {
                udp_server( aio, emPORT );
                std::this_thread::yield();
                udp_client( aio, emPORT );
            };

            // 事件循环，会阻塞直到退出或错误
            auto ret = aio->run( cb_entry );

            if( ret < 0 )
            {
                log_err( "aio error( %d ): %s", ret, aio->error() );
            }

            return result > 0;
        }
    
        /*********************************************************************
         * @brief 数据接收事件的处理回调
         */
        template< typename _chl >
        void on_read( _chl chl )
        {
            llog_tra( "<demo-udp-vlink>", "\t %s::%s()", typeid( *this ).name(), __func__ );

            // 数据接收缓存
            hude::buffer_t buffer;

            // 接收数据，返回值-1为没有数据可接收，0为网络断开了
            auto ret = chl->recv( buffer );
            if( ret < 1 ) return;

            // 打印数据
            buffer[ret] = 0;
            log_inf( "\t socket read %u: %s", buffer.size(), &(buffer[0]) );
    
            // 每次服务端与客户端来回发送时都少发一个字节
            if( ret > 1 )
            {
                ret = chl->send( &(buffer[0]), buffer.size() - 1U );

                if( ret > 0 ) result += 1;
            } else {
                // 终止网络事件泵
                aio->cancel();
            }
        }
    
        /*********************************************************************
         * @brief 网络关闭事件的处理回调
         */
        void on_close( net_t::sock_t sock )
        {
            llog_tra( "<demo-udp-vlink>", "\t %s::%s()", typeid( *this ).name(), __func__ );

            log_nte( "socket closed: %d", **sock );
        }
    
        /*********************************************************************
         * @brief UDP-vlink服务端测试
         */
        void udp_server( net_t::ctx_t aio, uint16_t port )
        {
            // 连接请求事件处理
            auto cb_accepted = [=]( const net_t::address_t& host, net_t::vlink_t link )
            {
                log_nte( "accepted client{ host: %s }", host.str().c_str() );

                // 绑定数据接收事件的处理回调
                link->set_on_recv( std::bind( &demo_udp_vlink_t::on_read< typename net_t::vlink_t >,  this, std::placeholders::_1 ) );
            };

            // 服务端开始监听端口
            auto service = net_t::io_obj< net_t::udp_vlink_t >( aio, "::0", port, cb_accepted );

            // 监听端口的关闭事件
            service->set_on_close( std::bind( &demo_udp_vlink_t::on_close, this, std::placeholders::_1 ) );

            bool state = service->state();

            if( ! state )
            {
                log_err( "socket error: %s", service->error() );
                service->close();

                this->result = -100;
            }
        }
 
        /*********************************************************************
        * @brief UDP-vlink客户端测试
        */
        void udp_client( net_t::ctx_t aio, uint16_t port )
        {
            // 连接请求事件处理
            auto cb_ready = [=]( net_t::sock_t sock )
            {
                log_nte( "connectd sock{ sock: %d }", **sock );

                // 绑定数据接收事件的处理回调
                sock->set_on_recv( std::bind( &demo_udp_vlink_t::on_read< typename net_t::sock_t >,  this, std::placeholders::_1 ) );

                // 发送数据
                const char* body = "123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789";
                sock->send( body, strlen( body ) );
            };

            // 服务端开始监听端口, 并加入epoll监听。
            // -- 默认收到第一次EPOLLOUT事件后不再监听该事件.
            // -- 如果要持续关注EPOLLOUT事件可在ready事件处理中修改： sock->attch( EPOLLIN | EPOLLOUT | EPOLLET )
            auto service = net_t::io_obj< net_t::udp_connect_t >( aio, "::1", port, cb_ready );

            // 绑定网络关闭事件的处理回调
            service->set_on_close( std::bind( &demo_udp_vlink_t::on_close, this, std::placeholders::_1 ) );

            bool state = service->state();

            if( ! state )
            {
                log_err( "socket error: %s", service->error() );
                service->close();

                this->result = -100;
            }
        }
    };

    //--------------------------------------------------------------------------------
    TEST_CASE_ADD( "udp-vlink", demo_udp_vlink_t, false );

} // namespace test

#endif //__demo_udp_vlink_kenny_kfjiweur89u83r33e3e__
